package ${package}.config.oauth;


import ${package}.service.impl.UserDetailsServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.Collections;

/**
 * 用于配置OAuth授权服务器的工作方式
 *
 * @author ${author}
 * @date ${date}
 */
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final AuthenticationManager authenticationManager;

    private final UserDetailsServiceImpl userDetailsService;

    private final JwtAccessTokenConverter jwtAccessTokenConverter;

    private final OauthProperties oauthProperties;

    private final CustomWebResponseExceptionHandler exceptionTranslator;

    /**
     * 配置客户端认证
     *
     * @param clients 客户端信息
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient(oauthProperties.getClientId())
                .resourceIds(oauthProperties.getResourceId())
                .secret(new BCryptPasswordEncoder().encode(oauthProperties.getSecret()))
                .authorizedGrantTypes(oauthProperties.getGrantType())
                //2小时
                .accessTokenValiditySeconds(oauthProperties.getAccessTokenValiditySeconds())
                //3天
                .refreshTokenValiditySeconds(oauthProperties.getRefreshTokenValiditySeconds())
                .scopes(oauthProperties.getScopes());
        super.configure(clients);
    }

    /**
     * 配置认证规则
     *
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
       //配置认证管理器
       endpoints.authenticationManager(authenticationManager)
                 //配置用户服务
                 .userDetailsService(userDetailsService)
                 .tokenStore(tokenStore())
                 .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                 //配置token存储的服务与位置
                 .tokenServices(defaultTokenServices())
                 .exceptionTranslator(exceptionTranslator);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
       security
               //允许表单认证
               .allowFormAuthenticationForClients()
               // 对于CheckEndpoint控制器[框架自带的校验]的/oauth/check端点允许所有客户端发送器请求而不会被Spring-security拦截
               .tokenKeyAccess("permitAll()")
               .checkTokenAccess("isAuthenticated()");
    }

    /**
     * 基于内存存储
     *
     * @return TokenStore
     */
    @Bean
    public TokenStore tokenStore() {
       return new InMemoryTokenStore();
    }

    @Primary
    @Bean
    public DefaultTokenServices defaultTokenServices() {
       DefaultTokenServices tokenServices = new DefaultTokenServices();
       //配置token存储
       tokenServices.setTokenStore(tokenStore());
       //开启支持refresh_token，此处如果之前没有配置，启动服务后再配置重启服务，可能会导致不返回token的问题，解决方式：清除redis对应token存储
       tokenServices.setSupportRefreshToken(true);
       // 令牌增强
       TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
       tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(jwtAccessTokenConverter));
       tokenServices.setTokenEnhancer(tokenEnhancerChain);
       //复用refresh_token
       tokenServices.setReuseRefreshToken(true);
       //token有效期，设置2小时
       tokenServices.setAccessTokenValiditySeconds(oauthProperties.getAccessTokenValiditySeconds());
       //refresh_token有效期，设置3天
       tokenServices.setRefreshTokenValiditySeconds(oauthProperties.getRefreshTokenValiditySeconds());
       return tokenServices;
   }
}
